package angels.dao;

import java.lang.reflect.Field;
import java.util.List;
import javax.persistence.EntityExistsException;
import javax.persistence.EntityManager;
import javax.persistence.Id;
import javax.persistence.NoResultException;
import javax.persistence.Query;

public class GenericDAO<C> implements Interface<C> {

    private EntityManager em = FabricaEntityManager.obterFactory().createEntityManager();

    public GenericDAO() {
    }

    public boolean iniciarTransacao() {
        try {
            if (em.getTransaction().isActive()) {
                return true;
            }
            em.getTransaction().begin();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean concluirTransacao() {
        try {
            em.getTransaction().commit();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    public boolean cancelarTransacao() {
        try {
            em.getTransaction().rollback();
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    @Override
    public boolean criar(C o) {
        try {

            this.iniciarTransacao();
            em.flush();
            em.persist(o);
            this.concluirTransacao();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            if (em.isOpen()) {
                em.getTransaction().rollback();
            }
            return false;
        }
    }

    @Override
    public boolean alterar(C o) {
        try {
            this.iniciarTransacao();
            em.merge(o);
            this.concluirTransacao();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            if (em.isOpen()) {
                em.getTransaction().rollback();
            }
            return false;
        }
    }

    @Override
    public boolean excluir(C o) {
        try {
            this.iniciarTransacao();
            em.remove(o);
            this.concluirTransacao();
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            if (em.isOpen()) {
                em.getTransaction().rollback();
            }
            return false;
        }
    }

    @Override
    public C obter(Class<C> classe, Object id) {
        if (id == null) {
            return null;
        }
        String query = classe.getSimpleName() + ".findById";
        final Query q = em.createNamedQuery(query);
        try {
            return (C) q.setParameter("id", id).getSingleResult();
        } catch (Exception e) {
            return null;
        }
    }

    @Override
    public List<C> obterTodos(Class<C> classe) {
        String query = classe.getSimpleName() + ".findAll";
        Query q = em.createNamedQuery(query);
        return (List<C>) q.getResultList();
    }

    public List<C> obterLista(Class<C> classe,
            List<String> criterios,
            List valores,
            final boolean AND) {
        if (criterios == null
                || valores == null
                || criterios.size() != valores.size()
                || criterios.size() < 1) {
            return null;
        }
        String query = "SELECT o FROM " + classe.getSimpleName() + " o WHERE ";
        query += " o." + criterios.get(0) + " = :" + criterios.get(0);
        final String CONECTIVO = AND ? " AND " : " OR ";
        for (int i = 1; i < criterios.size(); i++) {
            query += CONECTIVO + " o." + criterios.get(i) + " = :" + criterios.get(i);
        }
        final Query q = em.createQuery(query);
        try {
            for (int i = 0; i < valores.size(); i++) {
                q.setParameter(criterios.get(i), valores.get(i));
            }
            return (List<C>) q.getResultList();
        } catch (Exception e) {
            System.out.println("Erro: " + e);
            return null;
        }
    }

    /**
     * @return the entityManager
     */
    protected EntityManager getEntityManager() {
        return em;
    }

    /**
     * @param entityManager the entityManager to set
     */
    protected void setEntityManager(EntityManager entityManager) {
        this.em = entityManager;
    }

    @Override
    protected void finalize() throws Throwable {
        super.finalize();
        if (this.em != null && this.em.isOpen()) {
            this.em.flush();
            this.em.clear();
            this.em.close();
        }
    }

    public void refreshModel(Object o) {
        try {
            em.refresh(o);
        } catch (Exception e) {
        }
    }

    public List findByQueries(String consulta, int quantRegistros, int posicao) {
        if (quantRegistros == 0 && posicao == 0) {
            return em.createQuery(consulta).
                    getResultList();
        } else {
            return em.createQuery(consulta).
                    setMaxResults(quantRegistros).
                    setFirstResult(posicao).
                    getResultList();
        }
    }

    public Long getCountResult(String consulta) {
        int iFrom = consulta.toUpperCase().indexOf("FROM");
        String sResult = "select DISTINCT COUNT(o) " + consulta.substring(iFrom);
        Long quant = null;
        try {
            quant = (Long) em.createQuery(sResult).getSingleResult();
        } catch (NoResultException ex) {
            quant = 0L;
        }

        return quant;
    }

    public Object findByPrimaryKey(Class classe, Object obj) {
        Object o = em.find(classe, obj);
        return o;
    }

    public Object findByPrimaryKey(Class classe, Object obj, String condicao) {
        Field[] field = classe.getDeclaredFields();
        Field fieldChavePrimaria = null;

        for (Field f : field) {
            if (f.isAnnotationPresent(Id.class)) {
                fieldChavePrimaria = f;
                break;
            }
        }

        String consulta = "select o from " + classe.getSimpleName() + " o where o."
                + fieldChavePrimaria.getName() + " = :valor " + condicao;
        try {
            Object o = em.createQuery(consulta).setParameter("valor", obj).getSingleResult();
            return o;
        } catch (Exception e) {
            return null;
        }
    }

    public boolean create(Object obj) {

        try {
            em.getTransaction().begin();
            em.persist(obj);
            em.getTransaction().commit();
        } catch (EntityExistsException e) {
            System.out.print(e.getMessage());
            em.getTransaction().rollback();
            return false;
        } catch (Exception e) {
            System.out.print(e.getMessage());
            em.getTransaction().rollback();
            return false;
        }

        return true;
    }

    public boolean update(Object obj) {


        try {
            em.getTransaction().begin();
            em.merge(obj);
            em.getTransaction().commit();
        } catch (Exception e) {
            em.getTransaction().rollback();
            return false;
        }
        return true;
    }

    public boolean delete(Object obj) {

        try {
            em.getTransaction().begin();
            obj = em.merge(obj);
            em.remove(obj);
            em.getTransaction().commit();
        } catch (Exception e) {
            em.getTransaction().rollback();
            return false;
        }
        return true;
    }
}
